﻿//+-------------------------------------------------------------------------------------------------------------------------------------------------------
//
//Description:  ParametersViewModel 
//Author: Aravind Ramarathinam (aravr@microsoft.com)
//Copyright © Microsoft Corporation
//Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
//You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//
//THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
//INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. 
//See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
// 
//--------------------------------------------------------------------------------------------------------------------------------------------------------



using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Input;
using VmroleAuthor.Commands;
using VmroleAuthor.Models;

namespace VmroleAuthor.ViewModels
{
    public class ParametersViewModel : TreeViewItemViewModel, IDataErrorInfo, IProduceValidationResults
    {
        private readonly List<Parameter> parameters;        
        private readonly ObservableCollection<ParameterViewModel> observableParameters;
        private readonly TreeViewItemViewModel parametersContainerViewModel;

        public static string AutoGenerateMoniker = "[generate a new parameter]";
        public static string AutoGeneratedParamPrefix = "Generated";
        public static string VMRolePrefix = "VMRole";

        public static class PropertyNames
        {
            public const string Parameters = "Parameters";
            public const string Summary = "Summary";          
        }

        public ParametersViewModel(List<Parameter> parameters, TreeViewItemViewModel parametersContainerViewModel)
            : base(parametersContainerViewModel, true)
        {
            this.parametersContainerViewModel = parametersContainerViewModel;
            if (parameters == null)
            {
                parameters = new List<Parameter>();
                VmRoleResourceDefinitionViewModel vmd = this.parametersContainerViewModel as VmRoleResourceDefinitionViewModel;
                if (vmd != null)
                {
                    vmd.ResourceDefinition.ResourceParameters = parameters;
                }
                else
                {
                    VmRoleResourceExtensionViewModel vmr = this.parametersContainerViewModel as VmRoleResourceExtensionViewModel;
                    if (vmr != null)
                    {
                        vmr.ResourceExtensionDefinition.ResourceExtensionParameters = parameters;
                    }
                }
            }
            this.parameters = parameters;
            this.observableParameters = new ObservableCollection<ParameterViewModel>();           
            foreach (Parameter parameter in this.parameters)
            {         
                ViewDefParamInfo additionalInfo = new ViewDefParamInfo();
                additionalInfo.ViewDefParameterType = ParameterViewModel.GetViewDefParameterType(parameter.Type);
                ParameterViewModel parameterViewModel = new ParameterViewModel(parameter, additionalInfo, this.parametersContainerViewModel);
                this.observableParameters.Add(parameterViewModel);
            }         

            this.AddParameterCommand = new RelayCommand(
             param => this.AddObservableParameter(),
             param => this.CanAddObservableParameter());

            this.RemoveParameterCommand = new RelayCommand(
                param => this.RemoveObservableParameter((ParameterViewModel)param),
                param => this.CanRemoveObservableParameter((ParameterViewModel)param)); 
 
            this.ValidationResults = new ObservableCollection<CloudServiceModelValidationResult>();
        }      

        public List<string> AvailableParameters
        {
            get
            {
                List<string> availableParams = this.observableParameters.Select(parameter => parameter.FormattedName).ToList();
                availableParams.Add(AutoGenerateMoniker);
                return availableParams;
            }
        }

        public string Summary
        {
            get
            {               
                return "Parameters (" + this.Parameters.Count + ")";
            }
        }

        public ObservableCollection<ParameterViewModel> Parameters
        {
            get { return this.observableParameters; }
        }

        public ICommand AddParameterCommand { get; private set; }

        public ICommand RemoveParameterCommand { get; private set; }

        public bool CanAddObservableParameter()
        {
            return true;
        }
                    
        public void AddObservableParameter()
        {
            Parameter param = new Parameter();           
            ViewDefParamInfo additionalInfo = new ViewDefParamInfo {ViewDefParameterType = ViewDefParameterType.String};
            this.AddParameter(param, additionalInfo);
            //this.OnPropertyChanged(PropertyNames.Parameters);
        }

         public void AddParameter(Parameter param, ViewDefParamInfo additionalInfo)
         {
             this.parameters.Add(param);
             var paramViewModel = new ParameterViewModel(param, additionalInfo, this.parametersContainerViewModel);
             this.observableParameters.Add(paramViewModel);
             VmRoleResourceDefinitionViewModel vmd = this.parametersContainerViewModel as VmRoleResourceDefinitionViewModel;
             if (vmd != null)
             {
                 if (vmd.ViewDefViewModel != null)
                 {
                     vmd.ViewDefViewModel.AddViewDefParameter(paramViewModel);
                 }
             }
             this.OnPropertyChanged(PropertyNames.Parameters);
             this.OnPropertyChanged(PropertyNames.Summary);
         }

        public bool CanRemoveObservableParameter(ParameterViewModel observableParameter)
        {
            return true;
        }

        public void RemoveObservableParameter(ParameterViewModel observableParameter)
        {
            if (observableParameter != null)
            {
                this.parameters.Remove(observableParameter.Parameter);
                this.observableParameters.Remove(observableParameter);
                VmRoleResourceDefinitionViewModel vmd = this.parametersContainerViewModel as VmRoleResourceDefinitionViewModel;                       
                if (vmd != null)
                {
                     if (vmd.ViewDefViewModel != null)
                     {
                         vmd.ViewDefViewModel.RemoveViewDefParameter(observableParameter.Name);
                     }
                }
                this.OnPropertyChanged(PropertyNames.Summary);
                //this.OnPropertyChanged(PropertyNames.Parameters);
            }                    
        }

        public bool ContainsParameter(string parameterName)
        {         
            return null != this.observableParameters.FirstOrDefault(param => string.Equals(parameterName, param.Name, StringComparison.Ordinal));
        }

        public void AddParameterIfNotExist(string parameterName, string type, string description, ViewDefParamInfo viewDefParamInfo)
        {
             //Check if parameter exists            
             if (!this.ContainsParameter(parameterName))
             {
                 Parameter param = new Parameter { Name = parameterName, Type = type, Description = description };                              
                 this.AddParameter(param, viewDefParamInfo);            
             }
        }

        public void UpdateViewDefInfoFromParameterDefinitionContext(string parameterName, ViewDefParamInfo viewDefParamInfo)
        {
            var observableParam = this.observableParameters.FirstOrDefault(param => string.Equals(parameterName, param.Name, StringComparison.Ordinal));
            //if parameter exists            
            if (observableParam != null)
            {
                observableParam.UpdateViewDefInfoFromParameterDefinitionContext(viewDefParamInfo);             
            }
        }
            
        public void RemoveParameters(List<string> parameterNames)
        {
            if (parameterNames == null || parameterNames.Count == 0)
            {
                return;
            }
         
            //check for dependency/usage before removing, so remove only unused ones
            Dictionary<string, long> paramUsageCount = this.GetParameterUsageCount();            
            foreach (string parameterName in parameterNames)
            {
                 ParameterViewModel observableParameter = this.observableParameters.FirstOrDefault(param => string.Equals(parameterName, param.Name, StringComparison.Ordinal));
                 if (!paramUsageCount.ContainsKey(parameterName))
                 {
                     this.RemoveObservableParameter(observableParameter);
                 }                   
             }
        }       

        private Dictionary<string, long> GetParameterUsageCount()
        {        
            VmRoleResourceExtensionViewModel vmr = this.parametersContainerViewModel as VmRoleResourceExtensionViewModel;
            if (vmr != null)
            {
                return ParameterizationHelper.GetParameterUsageCount(vmr.ResourceExtensionDefinition);
            }
            VmRoleResourceDefinitionViewModel vmd = this.parametersContainerViewModel as VmRoleResourceDefinitionViewModel;
            if (vmd != null)
            {
                return ParameterizationHelper.GetParameterUsageCount(vmd.ResourceDefinition);
            }            
            return null;
        }

        public string this[string propertyName]
        {
            get
            {
               string error = null;
               switch (propertyName)
               {
                   case PropertyNames.Parameters:
                       // if no one is referencing this param, it will be flagged as error in ParametersViewModel                                   
                       StringBuilder sb = new StringBuilder();                                             
                       bool hasAtleastOneUsedParam = false;
                       // if no one is referencing this param, it will be flagged as error in ParametersViewModel                    
                       sb.Append("Delete unused parameters: ");
                       Dictionary<string, long> paramUsageCount = this.GetParameterUsageCount();
                       foreach (Parameter parameter in parameters)
                       {
                           if (!paramUsageCount.ContainsKey(parameter.Name))
                           {
                               hasAtleastOneUsedParam = true;
                               sb.AppendFormat("{0} ", parameter.Name);
                           }
                       }
                       if ( hasAtleastOneUsedParam)
                       {
                           error = sb.ToString();
                       }
                       break;
               }
                return error;
            }
        }

        public string Error { get; set; }

        public ObservableCollection<CloudServiceModelValidationResult> ValidationResults { get; private set; }

        public void UpdateValidationResults()
        {
            this.IsExpanded = true;
            ModelValidationResultsHelper.UpdateValidationResults(this, typeof(PropertyNames));
            //Add any other cross-field validations error           
        }        
    }   
}
